package POS_Controller;

import Cart.Cart;
import Customer.CustomerManager;
import DTO.Item;
import DTO.Customer;
import DAO.DAO;
import DTO.Category;
import DTO.Payment;
import Exception.KwickKioskDBException;
import Order.MakePayment;
import POS_Source.*;
import POS_Source.mainFrame;
import PaymentTest.PaymentStub;
import Session.POS_SessionManager;
import Session.SessionManager;
import java.awt.*;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;

/**
 * This class acts as controller , it is the central dispatcher of requests to
 * various service managers. so this class decouples service layer from view layer.
 * @author satkins
 * @see POS_Controller
 */
public class POSController {

    private mainFrame frame;
    private Container pane;
    private POSController posControl;
    private POS_SessionManager sessionManager;
    private DAO dao;

    /**
     * constructor for this class, initializes data access object
     */
    public POSController(){
        getConnection();
        sessionManager = new POS_SessionManager(dao);
    }

    public final void getConnection(){
        FileInputStream fstream = null;
        try {
            String location = System.getProperty("user.dir");
            fstream = new FileInputStream(location + "/DatabaseConfig.txt");
            // Get the object of DataInputStream
            DataInputStream in = new DataInputStream(fstream);
            BufferedReader br = new BufferedReader(new InputStreamReader(in));
            String strLine;
            strLine = br.readLine();
            StringTokenizer token = new StringTokenizer(strLine);
            token.nextToken();
            String userName = token.nextToken();
            strLine = br.readLine();
            token = new StringTokenizer(strLine);
            token.nextToken();
            String password = token.nextToken();
            strLine = br.readLine();
            token = new StringTokenizer(strLine);
            token.nextToken();
            String serverName = token.nextToken();
            try {
                
                dao = new DAO(userName, password, serverName);
            } catch (KwickKioskDBException ex) {
                //NEED TO IMPLEMENT ERROR HANDLING
                Logger.getLogger(POSController.class.getName()).log(Level.SEVERE, null, ex);
                if(frame == null){
                    System.out.println("caught db exception and am starting custom frame");
                    DatabaseDown down = new DatabaseDown(this);
                    down.setBounds(0, 0, 995, 755);
                    frame = new mainFrame(this, down);
                    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
                    frame.setLocation(((int) screenSize.getWidth() / 2) - frame.getWidth() / 2,
                            ((int) screenSize.getHeight() / 2) - frame.getHeight() / 2);
                    frame.setVisible(true);
                }
                else{
                    DatabaseDownScreen();
                }
            }
            
        } catch (IOException ex) {
            Logger.getLogger(POSController.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                fstream.close();
            } catch (IOException ex) {
                Logger.getLogger(POSController.class.getName()).log(Level.SEVERE, null, ex);
                
            }
        }
    }

    /**
     * this method initializes the main frame.
     */
    public void build() {
        if(frame == null){
        frame = new mainFrame(this);

        // get the content pane and set layout to null
        pane = frame.getContentPane();

        // set conditions of the frame      
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        frame.setLocation(((int) screenSize.getWidth() / 2) - frame.getWidth() / 2,
                ((int) screenSize.getHeight() / 2) - frame.getHeight() / 2);
        frame.setVisible(true);
        }
    }

    /* GETTERS AND SETTERS ****************************************************/
    /**
     * this method sets the logged in status of user in session
     * @param val
     */
    public void setLoggedIn(boolean val) {
        sessionManager.setLoggedIn(val);
    }

    /**
     * this method gets search results
     * @return a list of items
     */
    public ArrayList<Item> getSearchResults() {
        return sessionManager.getCart().getSearchResults();
    }

    /**
     * this method checks if user is logged in
     * @return a boolean flag
     */
    public boolean isLoggedIn() {
        return sessionManager.getLoggedIn();
    }

    /**
     * this method gets first name of user
     * @return string
     */
    public String getFirstName() {
        return sessionManager.getCustomer().getFirstName();
    }

    /**
     * this method gets number of items in cart 
     * @return string
     */
    public String getNumItemsInCart() {
        return Integer.toString(sessionManager.getCart().getTotalNumItems());
    }

    /**
     * this method gets cart object from session 
     * @return cart object
     */
    public Cart getCart() {
        return sessionManager.getCart();
    }

    /**
     * this method retrieves all categories 
     * @return list of categories
     */
    public ArrayList<Category> getCategories() {
        ArrayList<Category> categories = null;
        try {
            categories = dao.getCategories();
        } catch (KwickKioskDBException ex) {

            Logger.getLogger(POSController.class.getName()).log(Level.SEVERE, null, ex);
            DatabaseDownScreen();
        }
        return categories;
    }

    /**
     * this method return session manager
     * @return session manager
     */
    public POS_SessionManager getSessionManager() {
        return sessionManager;
    }


    /* FUNCTIONALITY **********************************************************/
    public boolean reconnect() {
        getConnection();
        if (dao == null)
            return false;
        sessionManager = new POS_SessionManager(dao);

        return true;
    }

    /**
     * this method searches an item using item code
     * @param word
     */
    public void searchByUPC(String word) {
        try {
            sessionManager.searchByUPC(word);
        } catch (KwickKioskDBException ex) {
            Logger.getLogger(POSController.class.getName()).log(Level.SEVERE, null, ex);
            DatabaseDownScreen();
        }
        SearchResultsScreen();
    }

    /**
     * this method searches the item using category id.
     * @param ctid
     */
    public void searchByCategory(int ctid) {
        try {
            sessionManager.searchByCategory(ctid);
        } catch (KwickKioskDBException ex) {
            Logger.getLogger(POSController.class.getName()).log(Level.SEVERE, null, ex);
        }
        SearchResultsScreen();
    }

    /**
     * this method add an item to cart and updates the cart in session.
     * @param item
     * @param quantity
     */
    public void addItemToCart(Item item, int quantity) {
        try {
            sessionManager.addItemToCart(item, quantity);
        } catch (KwickKioskDBException ex) {
            Logger.getLogger(POSController.class.getName()).log(Level.SEVERE, null, ex);
            DatabaseDownScreen();
        }
    }

    /**
     * this method add store card and updates the session.
     * @param storeCard
     * @return
     */
    public boolean addStoreCard(String storeCard) {
        try {
            if (sessionManager.setCustomer(storeCard)) {
                mainFrame();
                CartScreen();
                return true;
            } else {
                return false;
            }
        } catch (KwickKioskDBException ex) {
            Logger.getLogger(POSController.class.getName()).log(Level.SEVERE, null, ex);
            DatabaseDownScreen();
        }
        return false;
    }

    /**
     * this method removes store card and clears off the session
     */
    public void removeStoreCard() {
        try {
            sessionManager.logout();
        } catch (KwickKioskDBException ex) {
            Logger.getLogger(POSController.class.getName()).log(Level.SEVERE, null, ex);
            DatabaseDownScreen();
        }
        mainFrame();
    }

    /**
     * this method sets clerk login status in session
     * @param id a string
     * @param password a string
     * @return boolean flag
     */
    public boolean clerkLogin(String id, String password) {
        return sessionManager.clerkLogIn(id, password);
    }

    /**
     * this method sets clerk log out status in session
     */
    public void clerkLogout() {
        sessionManager.clerkLogout();
        mainFrame();
    }

    public void applyDiscount() {
    }

    /**
     * this method handles make credit card  payment request and directs the flow
     * to payment processing screen
     * @param amount
     */
    public void makeCCPayment(double amount) {
        PaymentInProcessScreen(amount);
    }

    /**
     * this method handles cash payment request and directs the flow to finish transaction if
     * payment received else to select payment screen
     * @param amount
     */
    public void makeCashPayment(double amount) {
        PaymentStub makePayment = new PaymentStub();
        Payment pymnt = makePayment.makeCashPayment(amount);
        sessionManager.addPayment(pymnt);
        if (sessionManager.enoughPayment()) {
            finishTransaction();
        } else {
            PaymentSelectScreen();
        }
    }

    /**
     * this method handles cash payment request and directs the flow to finish transaction if
     * payment received else to select payment screen
     * @param amount
     */
    public void makePayment(double amount) {

        PaymentStub makePayment = new PaymentStub();
        Payment pymnt = makePayment.makeCreditCardPayment(amount);
        sessionManager.addPayment(pymnt);
        if (sessionManager.enoughPayment()) {
            finishTransaction();
           
        } else {
            PaymentSelectScreen();
        }
    }

    /**
     * this method handles finish transaction request
     */
    private void finishTransaction() {
        sessionManager.fullfillOrder();
        //check for error here
        ConfirmPaymentScreen();

    }

    public boolean quantityAvailable(String upc, int qty) {
        boolean result = true;
        int inStore = 0;
        try {
            inStore = dao.getItemQtyInStore("Webstore", upc);
            System.out.println("requested upc = " + upc + " requsted  qty " + Integer.toString(qty) + " available qty = " + inStore);
        } catch (KwickKioskDBException ex) {
            Logger.getLogger(POSController.class.getName()).log(Level.SEVERE, null, ex);
            // Implement connection down function
        }
        if (qty + 1 > inStore) {
            result = false;
        }
        return result;
    }

    public void newSession(){
        sessionManager = new POS_SessionManager(dao);
        refresh();
        welcomeFrame();
    }
    /** SCREENS ***************************************************************/
    /**
     * this method refreshes the screen 
     */
    public void refresh() {
        frame.setTotals();
        frame.repaint();
    }

    /**
     * this method directs the control to search by item code  view
     */
    public void POSSearchByUPCScreen() {
        POS_SearchByUPC search = new POS_SearchByUPC(this);
        frame.changeScreen(search);
        pane.validate();
        pane.repaint();
    }

    /**
     * this method directs the control to add store card screen
     */
    public void AddStoreCardScreen() {
        POS_CustomerLogin addCard = new POS_CustomerLogin(this);
        frame.changeScreen(addCard);
        pane.validate();
        pane.repaint();
    }

    /**
     * this method directs the control to search results screen
     */
    public void SearchResultsScreen() {
        POS_SearchResults resultsScreen = new POS_SearchResults(this);
        frame.changeScreen(resultsScreen);
        pane.validate();
        pane.repaint();
    }

    /**
     * this method directs the control to item description view
     * @param selection
     */
    public void ItemDescriptionScreen(Item selection) {

        POS_ItemDescription itemDescription = new POS_ItemDescription(this, selection);
        frame.changeScreen(itemDescription);
        pane.validate();
        pane.repaint();
    }

    /**
     * this method directs the control to search by choice view 
     */
    public void SearchChoiceScreen() {
        POS_SearchChoice choice = new POS_SearchChoice(this);
        frame.changeScreen(choice);
        pane.validate();
        pane.repaint();
    }

    /**
     * this method directs the control to search by category  view
     */
    public void SearchCategoryScreen() {
        POS_SearchByCategory categoryScreen = new POS_SearchByCategory(this);
        frame.changeScreen(categoryScreen);
        pane.validate();
        pane.repaint();
    }

    /**
     * this method directs the control to cart  view
     */
    public void CartScreen() {
        POS_ViewCart cartScreen = new POS_ViewCart(this);
        frame.changeScreen(cartScreen);

        pane.validate();
        pane.repaint();
    }

    /**
     * this method directs the control to clerk login  view
     */
    public void ClerkLoginScreen() {
        POS_ClerkLogin login = new POS_ClerkLogin(this);
        frame.changeScreen(login);
        pane.validate();
        pane.repaint();
    }

    /**
     * this method directs the control to clerk discount view 
     */
    public void ClerkDiscountScreen() {
        POS_ClerkDiscount login = new POS_ClerkDiscount(this);
        frame.changeScreen(login);
        pane.validate();
        pane.repaint();
    }

    /**
     * this method directs the control to select payment  view
     */
    public void PaymentSelectScreen() {
        POS_PaymentSelect select = new POS_PaymentSelect(this);
        select.setBounds(0, 0, 995, 755);
        frame.changeFrame(select);
    }

    /**
     * this method directs the control to cart amount screen
     */
    public void CardAmountScreen() {
        POS_CheckoutCardAmount cardAmount = new POS_CheckoutCardAmount(this);
        cardAmount.setBounds(0, 0, 995, 755);
        frame.changeFrame(cardAmount);
    }

    /**
     *this method directs the control to process payment  view
     * @param amount
     */
    public void PaymentInProcessScreen(double amount) {
        POS_CheckoutCard inProcess = new POS_CheckoutCard(this, amount);
        inProcess.setBounds(0, 0, 995, 755);
        frame.changeFrame(inProcess);

    }

    /**
     * this method directs the control to confirm payment view
     */
    public void ConfirmPaymentScreen() {
        POS_PrintReceipt success = new POS_PrintReceipt(this);
        success.setBounds(0, 0, 995, 755);
        frame.changeFrame(success);
    }

    /**
     * this method directs the control to override price view
     * @param item
     */
    public void OverridePriceScreen(Item item) {
        POS_ClerkPrice newPrice = new POS_ClerkPrice(this, item);
        frame.changeScreen(newPrice);
        pane.validate();
        pane.repaint();
    }

    /**
     * this method directs the control to payment by cash view
     */
    public void CashPaymentScreen() {
        CashSimulator cash = new CashSimulator(this);
        cash.setBounds(0, 0, 995, 755);
        frame.changeFrame(cash);
    }

    public void DatabaseDownScreen() {
        DatabaseDown down = new DatabaseDown(this);
        down.setBounds(0, 0, 995, 755);
        frame.changeFrame(down);
    }

    /**
     * this method directs the control to main out  view
     */
    public void mainFrame() {
        frame.mainOutScreen();
    }

    /**
     * this method directs the control to welcome  view
     */
    public void welcomeFrame() {
        POS_Welcome welcome = new POS_Welcome(this);
        welcome.setBounds(0, 0, 995, 755);
        frame.changeFrame(welcome);
    }
}
